﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Linq.Expressions;
using System.Windows.Threading;

namespace de.codingfreaks.libs.cfThreadingTools
{
    /// <summary>
    /// Base class for all threading-utilities.
    /// </summary>
    public class BaseTools
    {

        #region static methods

        /// <summary>
        /// Sets the property of a control and checks for InvokeRequiered.
        /// </summary>
        /// <param name="ctlTarget">The control which holds the property.</param>
        /// <param name="strPropertyName">The name of the property (case-sensitive!).</param>
        /// <param name="objValue">An object which includes the new value of the property.</param>
        public static void SetPropertyValue(Control ctlTarget,
                                            string strPropertyName,
                                            object objValue)
        {
            if (ctlTarget.InvokeRequired)
            {
                // the current thread is not the thread of the control -> invoke
                SetPropertyValueDelegate d = new SetPropertyValueDelegate(SetPropertyValue);
                ctlTarget.Invoke(d, ctlTarget, strPropertyName, objValue);
            }
            else
            {
                // the current thread is the thread of the control -> make the change
                Tools.SetPropertyValue(ctlTarget, strPropertyName, objValue, null);
            }
        }

        /// <summary>
        /// Sets the property of a WPF control and checks against the DispatcherObject.
        /// </summary>
        /// <param name="dobjTarget">The WPF control which holds the property.</param>
        /// <param name="strPropertyName">The name of the property (case-sensitive!).</param>
        /// <param name="objValue">An object which includes the new value of the property.</param>
        public static void SetWpfPropertyValue(DispatcherObject dobjTarget,
                                               string strPropertyName,
                                               object objValue)
        {
            if (!dobjTarget.Dispatcher.CheckAccess())
            {
                // current thread can't access the target
                SetWpfPropertyValueDelegate d = new SetWpfPropertyValueDelegate(SetWpfPropertyValue);
                dobjTarget.Dispatcher.Invoke(d, dobjTarget, strPropertyName, objValue);
            }
            else
            {
                // current thread can access the target
                Tools.SetPropertyValue(dobjTarget, strPropertyName, objValue, null);
            }
        }

        /// <summary>
        /// Sets the property of an object checking a control for InvokeRequired.
        /// </summary>                
        /// <param name="ctlTarget">The control which should be checked for InvokeRequired.</param>
        /// <param name="objPropertyObject">The object which holds the property.</param>
        /// <param name="strPropertyName">The name of the property (case-sensitive!).</param>
        /// <param name="objValue">An object which includes the new value of the property.</param>
        public static void SetSubPropertyValue(Control ctlTarget,
                                               object objPropertyObject,
                                               string strPropertyName,
                                               object objValue)
        {
            if (ctlTarget.InvokeRequired)
            {
                // the current thread is not the thread of the control -> invoke
                SetSubPropertyValueDelegate d = new SetSubPropertyValueDelegate(SetSubPropertyValue);
                ctlTarget.Invoke(d, ctlTarget, objPropertyObject, strPropertyName, objValue);
            }
            else
            {
                // the current thread is the thread of the control -> make the change
                Tools.SetPropertyValue(objPropertyObject, strPropertyName, objValue, null);
            }
        }

        /// <summary>
        /// Sets the property of an object checking a WPF control for its Dispatcher.
        /// </summary>                
        /// <param name="dobjTarget">The WPF control which holds the property.</param>
        /// <param name="objPropertyObject">The object which holds the property.</param>
        /// <param name="strPropertyName">The name of the property (case-sensitive!).</param>
        /// <param name="objValue">An object which includes the new value of the property.</param>
        public static void SetWpfSubPropertyValue(DispatcherObject dobjTarget,
                                                  object objPropertyObject,
                                                  string strPropertyName,
                                                  object objValue)
        {
            if (!dobjTarget.Dispatcher.CheckAccess())
            {
                // the current thread is not the thread of the control -> invoke
                SetWpfSubPropertyValueDelegate d = new SetWpfSubPropertyValueDelegate(SetWpfSubPropertyValue);
                dobjTarget.Dispatcher.Invoke(d, dobjTarget, objPropertyObject, strPropertyName, objValue);
            }
            else
            {
                // the current thread is the thread of the control -> make the change
                Tools.SetPropertyValue(dobjTarget, strPropertyName, objValue, null);
            }
        }

        /// <summary>
        /// Invoke a method of a control and checks for InvokeRequired.
        /// </summary>
        /// <param name="ctlTarget">The control which's method should be invoked.</param>
        /// <param name="strMethodName">The name of the method (case-sensitive!).</param>
        /// <param name="aobjParameters">The parameters to pass to the method.</param>
        public static object InvokeMethod(Control ctlTarget,
                                          string strMethodName,
                                          params object[] aobjParameters)
        {
            if (ctlTarget.InvokeRequired)
            {
                // the current thread is not the thread of the control -> invoke
                InvokeMethodDelegate d = new InvokeMethodDelegate(InvokeMethod);
                return ctlTarget.Invoke(d, ctlTarget, strMethodName, aobjParameters);                
            }
            else
            {
                // the current thread is the thread of the control -> call the method
                return Tools.CallMethod(ctlTarget, strMethodName, aobjParameters);
            }            
        }

        /// <summary>
        /// Invoke a method of a WPF control and checks for CheckAccess.
        /// </summary>
        /// <param name="dobjTarget">The control which's method should be invoked.</param>
        /// <param name="strMethodName">The name of the method (case-sensitive!).</param>
        /// <param name="aobjParameters">The parameters to pass to the method.</param>
        public static object InvokeWpfMethod(DispatcherObject dobjTarget,
                                             string strMethodName,
                                             params object[] aobjParameters)
        {
            if (!dobjTarget.Dispatcher.CheckAccess())
            {
                // the current thread is not the thread of the control -> invoke
                InvokeWpfMethodDelegate d = new InvokeWpfMethodDelegate(InvokeWpfMethod);
                return dobjTarget.Dispatcher.Invoke(d, dobjTarget, strMethodName, aobjParameters);
            }
            else
            {
                // the current thread is the thread of the control -> call the method
                return Tools.CallMethod(dobjTarget, strMethodName, aobjParameters);
            }
        }
        
        /// <summary>
        /// Invokes a method of an object and checks a control for InvokeRequired.
        /// </summary>
        /// <example>BaseTools.InvokeSubMethod(listView, listView.Items, "Add", listViewItem);</example>
        /// <param name="ctlTarget">The control which should be checked for InvokeRequired.</param>
        /// <param name="objPropertyObject">The object which's method should be invoked.</param>
        /// <param name="strMethodName">The name of the method (case-sensitive!).</param>
        /// <param name="aobjParameters">The parameters to pass to the method.</param>
        public static object InvokeSubMethod(Control ctlTarget,
                                             object objPropertyObject,
                                             string strMethodName,
                                             params object[] aobjParameters)
        {
            if (ctlTarget.InvokeRequired)
            {
                // the current thread is not the thread of the control -> invoke
                InvokeSubMethodDelegate d = new InvokeSubMethodDelegate(InvokeSubMethod);
                return ctlTarget.Invoke(d, ctlTarget, objPropertyObject, strMethodName, aobjParameters);
            }
            else
            {
                // the current thread is the thread of the control -> call the method
                return Tools.CallMethod(objPropertyObject, strMethodName, aobjParameters);
            }            
        }

        /// <summary>
        /// Invokes a method of an object and checks a WPF control for CheckAccess.
        /// </summary>        
        /// <param name="dobjTarget">The WPF control which should be checked for CheckAccess.</param>
        /// <param name="objPropertyObject">The object which's method should be invoked.</param>
        /// <param name="strMethodName">The name of the method (case-sensitive!).</param>
        /// <param name="aobjParameters">The parameters to pass to the method.</param>
        public static object InvokeWpfSubMethod(DispatcherObject dobjTarget,
                                                object objPropertyObject,   
                                                string strMethodName,
                                                params object[] aobjParameters)
        {
            if (!dobjTarget.Dispatcher.CheckAccess())
            {
                // the current thread is not the thread of the control -> invoke
                InvokeWpfSubMethodDelegate d = new InvokeWpfSubMethodDelegate(InvokeWpfSubMethod);
                return dobjTarget.Dispatcher.Invoke(d, dobjTarget, objPropertyObject, strMethodName, aobjParameters);
            }
            else
            {
                // the current thread is the thread of the control -> call the method
                return Tools.CallMethod(objPropertyObject, strMethodName, aobjParameters);
            }
        }

        #endregion

    }
}
